package cn.myfirefly.utils.mail;

import java.util.Calendar;
import java.util.List;
import java.util.Properties;

import javax.activation.DataHandler;
import javax.activation.FileDataSource;
import javax.mail.Address;
import javax.mail.BodyPart;
import javax.mail.Message;
import javax.mail.Multipart;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import javax.mail.internet.MimeUtility;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import cn.myfirefly.utils.Prop;
import cn.myfirefly.utils.PropUtil;
import cn.myfirefly.utils.StrUtil;

/**
 * 邮件发送器
 * 
 * @author zhouxiaoming
 *
 */
public class MailSender {
	private static Logger logger = LoggerFactory.getLogger(MailSender.class);
	
//	private static MailSender sender = null;
//
//    public static MailSender getInstance() {
//        if(sender == null){
//            sender = new MailSender();
//        }
//        return sender;
//    }

    /**
     * 以文本格式发送邮件
     * 
     * @param mailConfigInfo
     *            邮箱配置信息
     * @param mailInfo
     *            邮件信息
     * @param mailType
     *            邮件类型 1-error；2-warning；3-notify；
     * @return
     * @throws Exception
     */
    public static boolean sendTextMail(MailConfigInfo mailConfigInfo, MailInfo mailInfo, int mailType) throws Exception {

        // 需要身份认证，创建一个密码验证器
        MailAuthenticator authenticator = new MailAuthenticator(mailConfigInfo.getUsername(), mailConfigInfo.getPassword());

        try {
        	Properties prop = mailConfigInfo.getProperties();
        	// 根据邮件会话属性和密码验证器构造一个发送邮件的session
        	Session sendMailSession = Session.getDefaultInstance(prop, authenticator);
            
        	Message mailMessage = getTextMessage(sendMailSession, mailConfigInfo, mailInfo, mailType);
        	
            // 发送邮件
            Transport.send(mailMessage);

        } catch (Exception e) {
        	logger.error(e.getMessage(), e);
            return false;
        }

        return true;
    }

    /**
     * 以文本格式发送邮件，适合连接一次，发送多个邮件的情景。
     * 
     * @param mailConfigInfo
     *            邮箱配置信息
     * @param emailList
     *            邮件信息列表
     * @param mailType
     *            邮件类型 1-error；2-warning；（默认）3-notify；
     * @return
     * @throws Exception
     */
    public static boolean sendTextMail(MailConfigInfo mailConfigInfo, List<MailInfo> emailList, int mailType) throws Exception {

        // 需要身份认证，创建一个密码验证器
        MailAuthenticator authenticator = new MailAuthenticator(mailConfigInfo.getUsername(), mailConfigInfo.getPassword());
        Transport transport = null;
        
        try {
        	Properties prop = mailConfigInfo.getProperties();
        	// 根据邮件会话属性和密码验证器构造一个发送邮件的session
        	Session sendMailSession = Session.getDefaultInstance(prop, authenticator);
            
        	transport = sendMailSession.getTransport();
    		
    		transport.connect();
    		
        	for (MailInfo mailInfo : emailList) {
        		Message mailMessage = getTextMessage(sendMailSession, mailConfigInfo, mailInfo, mailType);
        		
        		// 发送邮件
        		transport.sendMessage(mailMessage, mailMessage.getAllRecipients());
			}
        	
        } catch (Exception e) {
        	logger.error(e.getMessage(), e);
            return false;
        } finally {
        	if (transport != null) {
				transport.close();
			}
		}

        return true;
    }
    
    /**
     * 以HTML格式发送邮件
     * 
     * @param mailConfigInfo
     *            邮箱配置信息
     * @param mailInfo
     *            邮件信息
     * @param mailType
     *            邮件类型 1-error；2-warning；（默认）3-notify；
     * @return
     * @throws Exception
     */
    public static boolean sendHtmlMail(MailConfigInfo mailConfigInfo, MailInfo mailInfo, int mailType) throws Exception {

        // 需要身份认证，创建一个密码验证器
        MailAuthenticator authenticator = new MailAuthenticator(mailConfigInfo.getUsername(), mailConfigInfo.getPassword());

        try {
        	Properties prop = mailConfigInfo.getProperties();
        	// 根据邮件会话属性和密码验证器构造一个发送邮件的session
        	Session sendMailSession = Session.getDefaultInstance(prop, authenticator);
        	
        	Message mailMessage = getHTMLMessage(sendMailSession, mailConfigInfo,mailInfo, mailType);

            // 发送邮件
            Transport.send(mailMessage);

        } catch (Exception e) {
        	logger.error(e.getMessage(), e);
            throw e;
        }

        return true;
    }
    
    /**
     * 以HTML格式发送邮件，适合连接一次，发送多个邮件的情景。
     * 
     * @param mailConfigInfo
     *            邮箱配置信息
     * @param emailList
     *            邮件信息列表
     * @param mailType
     *            邮件类型 1-error；2-warning；（默认）3-notify；
     * @return
     * @throws Exception
     */
    public static boolean sendHtmlMail(MailConfigInfo mailConfigInfo, List<MailInfo> emailList, int mailType) throws Exception {
    	
    	// 需要身份认证，创建一个密码验证器
    	MailAuthenticator authenticator = new MailAuthenticator(mailConfigInfo.getUsername(), mailConfigInfo.getPassword());
    	Transport transport = null;
    	try {
    		Properties prop = mailConfigInfo.getProperties();
    		// 根据邮件会话属性和密码验证器构造一个发送邮件的session
    		Session sendMailSession = Session.getDefaultInstance(prop, authenticator);
    		
    		transport = sendMailSession.getTransport();
    		
    		transport.connect();
    		
    		Message mailMessage = null;
    		for (MailInfo mailInfo : emailList) {
    			mailMessage = getHTMLMessage(sendMailSession, mailConfigInfo,mailInfo, mailType);
    			// 发送邮件
    			transport.sendMessage(mailMessage, mailMessage.getAllRecipients());
			}
    		
    	} catch (Exception e) {
    		logger.error(e.getMessage(), e);
    		throw e;
    	} finally {
			if (transport != null) {
				transport.close();
			}
		}
    	
    	return true;
    }

    /**
     * 以图片为正文格式发送邮件
     * 
     * @param mailConfigInfo
     *            邮箱配置信息
     * @param mailInfo
     *            邮件信息
     * @param mailType
     *            邮件类型 1-error；2-warning；（默认）3-notify；
     * @return
     * @throws Exception
     */
    public static boolean sendImageMail(MailConfigInfo mailConfigInfo, MailInfo mailInfo, int mailType) throws Exception {

        // 需要身份认证，创建一个密码验证器
        MailAuthenticator authenticator = new MailAuthenticator(mailConfigInfo.getUsername(), mailConfigInfo.getPassword());

        try {
        	Properties prop = mailConfigInfo.getProperties();
        	// 根据邮件会话属性和密码验证器构造一个发送邮件的session
        	Session sendMailSession = Session.getDefaultInstance(prop, authenticator);
            
        	Message mailMessage = getImageMessage(sendMailSession, mailConfigInfo, mailInfo, mailType);
        	
            // 发送邮件
            Transport.send(mailMessage);

        } catch (Exception e) {
        	logger.error(e.getMessage(), e);
            throw e;
        }

        return true;
    }
    
    /**
     * 以图片为正文格式发送邮件，适合连接一次，发送多个邮件的情景。
     * 
     * @param mailConfigInfo
     *            邮箱配置信息
     * @param emailList
     *            邮件信息列表
     * @param mailType
     *            邮件类型 1-error；2-warning；（默认）3-notify；
     * @return
     * @throws Exception
     */
    public static boolean sendImageMail(MailConfigInfo mailConfigInfo, List<MailInfo> emailList, int mailType) throws Exception {
    	// 需要身份认证，创建一个密码验证器
    	MailAuthenticator authenticator = new MailAuthenticator(mailConfigInfo.getUsername(), mailConfigInfo.getPassword());
    	Transport transport = null;
    	try {
    		Properties prop = mailConfigInfo.getProperties();
    		// 根据邮件会话属性和密码验证器构造一个发送邮件的session
    		Session sendMailSession = Session.getDefaultInstance(prop, authenticator);
    		
    		transport = sendMailSession.getTransport();
    		
    		transport.connect();
    		
    		Message mailMessage = null;
    		for (MailInfo mailInfo : emailList) {
    			mailMessage = getImageMessage(sendMailSession, mailConfigInfo,mailInfo, mailType);
    			// 发送邮件
    			transport.sendMessage(mailMessage, mailMessage.getAllRecipients());
			}
    		
    	} catch (Exception e) {
    		logger.error(e.getMessage(), e);
    		throw e;
    	} finally {
			if (transport != null) {
				transport.close();
			}
		}
    	
    	return true;
    }
    
    /**
     * 发送邮件，连接一次服务器，然后循环发送，发送时可通过MailSenderListener监听邮件发送结果
     * 
     * @param mailConfigInfo
     * @param emailList
     * @param mailType
     * @param mailSenderListener
     * @return
     * @throws Exception
     */
    public static boolean sendMail(MailConfigInfo mailConfigInfo, List<MailInfo> emailList, int mailType, MailSenderListener mailSenderListener) throws Exception {
    	// 需要身份认证，创建一个密码验证器
    	MailAuthenticator authenticator = new MailAuthenticator(mailConfigInfo.getUsername(), mailConfigInfo.getPassword());
    	Transport transport = null;
    	MailInfo mailInfoFail = null;
		Properties prop = mailConfigInfo.getProperties();
		// 根据邮件会话属性和密码验证器构造一个发送邮件的session
		Session sendMailSession = Session.getDefaultInstance(prop, authenticator);
		transport = sendMailSession.getTransport();
		transport.connect();
		int index = 0;
		try {
    		Message mailMessage = null;
    		for (MailInfo mailInfo : emailList) {
    			mailInfoFail = mailInfo;
    			if (mailInfo.getMessageType() == MailInfo.MESSAGE_TYPE_HTML) {
    				mailMessage = getHTMLMessage(sendMailSession, mailConfigInfo,mailInfo, mailType);
				} else if (mailInfo.getMessageType() == MailInfo.MESSAGE_TYPE_IMAGE) {
					mailMessage = getImageMessage(sendMailSession, mailConfigInfo,mailInfo, mailType);
				} else {
					mailMessage = getTextMessage(sendMailSession, mailConfigInfo,mailInfo, mailType);
				}
    			// 发送邮件
    			transport.sendMessage(mailMessage, mailMessage.getAllRecipients());
    			if (mailSenderListener != null) {
    				mailSenderListener.success(index, mailInfo);
				}
    			index++;
			}
    	} catch (Exception e) {
    		logger.error(e.getMessage(), e);
    		if (mailSenderListener != null) {
    			mailSenderListener.fail(index, mailInfoFail, e);
			}
    		throw e;
    	} finally {
			if (transport != null) {
				transport.close();
			}
		}
    	
    	return true;
    }
    
    /**
     * 创建发送邮件列表地址对象
     * 
     * @param mailInfo
     *            邮件信息
     * @param mailType
     *            邮件类型 1-error；2-warning；（默认）3-notify；
     * @return Address[0]：发送地址数组；Address[1]：抄送地址数组
     */
    private static Address[][] getMailToAddress(MailInfo mailInfo, int mailType) throws AddressException {
        Address[] toAdds = null;
        Address[] ccAdds = null;

        String[] toMails = null;
        if (mailType == 1) {
        	toMails = mailInfo.getErrorTo().split(";");
		} else if (mailType == 2) {
			toMails = mailInfo.getWarningTo().split(";");
		} else {
			toMails = mailInfo.getNotifyTo().split(";");
		}
        toAdds = new InternetAddress[toMails.length];
        for (int index = 0; index < toMails.length; index++) {
            toAdds[index] = new InternetAddress(toMails[index]);
        }
        
        if (StrUtil.notBlank(mailInfo.getNotifyCc())) {
        	String[] ccMails = null;
            if (mailType == 1) {
            	ccMails = mailInfo.getErrorCc().split(";");
    		} else if (mailType == 2) {
    			ccMails = mailInfo.getWarningCc().split(";");
    		} else {
    			ccMails = mailInfo.getNotifyCc().split(";");
    		}
        	ccAdds = new InternetAddress[ccMails.length];
        	for (int index = 0; index < ccMails.length; index++) {
        		ccAdds[index] = new InternetAddress(ccMails[index]);
        	}
		}

        Address[][] result = { toAdds, ccAdds };
        return result;
    }
    
    /**
     * 构建MailConfigInfo对象
     * @return
     */
    public static MailConfigInfo getMailConfigInfo() {
    	Prop prop = PropUtil.use("config.properties");
    	MailConfigInfo info = new MailConfigInfo();
        info.setMailHost(prop.get("mail.host"));
        info.setMailPort(prop.get("mail.port"));
        info.setMailSsl(prop.getBoolean("mail.ssl"));
        if (StrUtil.isBlank(prop.get("mail.tls"))) {
        	info.setMailTls(true);
		} else {
			info.setMailTls(prop.getBoolean("mail.tls"));
		}
        info.setUsername(prop.get("mail.username"));
        info.setPassword(prop.get("mail.password"));
        return info;
    }
    
    private static Message getHTMLMessage(Session sendMailSession, MailConfigInfo mailConfigInfo, MailInfo mailInfo, int mailType) throws Exception {
    	// 根据session创建一个邮件消息
		Message mailMessage = new MimeMessage(sendMailSession);
		// 创建邮件发送者地址
		Address from = new InternetAddress(mailConfigInfo.getUsername());
		// 设置邮件消息的发送者
		mailMessage.setFrom(from);
		
		// 创建邮件的接收者地址 to：发送；cc：抄送
		Address[][] maillToArr = getMailToAddress(mailInfo, mailType);
		
		// 设置邮件消息的接收者，发送，抄送
		if (maillToArr != null && maillToArr[0] != null && maillToArr[0].length > 0) {
			mailMessage.setRecipients(Message.RecipientType.TO, maillToArr[0]);
		}
		if (maillToArr != null && maillToArr[1] != null && maillToArr[1].length > 0) {
			mailMessage.setRecipients(Message.RecipientType.CC, maillToArr[1]);
		}
		
		// 设置邮件消息的主题
		mailMessage.setSubject(mailInfo.getSubject());
		// 设置邮件消息发送的时间
		mailMessage.setSentDate(Calendar.getInstance().getTime());
		
		// MimeMultipart类是一个容器类，包含MimeBodyPart类型的对象
		Multipart multiPart = new MimeMultipart();
		// 创建一个包含HTML内容的MimeBodyPart
		BodyPart bodyPart = new MimeBodyPart();
		// 设置html邮件消息内容
		bodyPart.setContent(mailInfo.getContent(), "text/html; charset=utf-8");
		multiPart.addBodyPart(bodyPart);
		
		//添加附件
		if(mailInfo.getAttachFileNames() != null && mailInfo.getAttachFileNames().length != 0){
			for(String attachFile : mailInfo.getAttachFileNames()){
				bodyPart=new MimeBodyPart();
				//得到数据源  
				FileDataSource fds=new FileDataSource(attachFile);
				//得到附件本身并放入BodyPart
				bodyPart.setDataHandler(new DataHandler(fds));
				//得到文件名并编码（防止中文文件名乱码）同样放入BodyPart
				bodyPart.setFileName(MimeUtility.encodeText(fds.getName()));  
				multiPart.addBodyPart(bodyPart);  
			}
		}
		
		// 设置邮件消息的主要内容
		mailMessage.setContent(multiPart);
		
		return mailMessage;
    }
    
    private static Message getTextMessage(Session sendMailSession, MailConfigInfo mailConfigInfo, MailInfo mailInfo, int mailType) throws Exception {
    	// 根据session创建一个邮件消息
        Message mailMessage = new MimeMessage(sendMailSession);
        // 创建邮件发送者地址
        Address from = new InternetAddress(mailConfigInfo.getUsername());
        // 设置邮件消息的发送者
        mailMessage.setFrom(from);

        // 创建邮件的接收者地址 to：发送；cc：抄送
        Address[][] maillToArr = getMailToAddress(mailInfo, mailType);

        // 设置邮件消息的接收者，发送，抄送
        if (maillToArr != null && maillToArr[0] != null && maillToArr[0].length > 0) {
            mailMessage.setRecipients(Message.RecipientType.TO, maillToArr[0]);
        }
        if (maillToArr != null && maillToArr[1] != null && maillToArr[1].length > 0) {
            mailMessage.setRecipients(Message.RecipientType.CC, maillToArr[1]);
        }

        // 设置邮件消息的主题
        mailMessage.setSubject(mailInfo.getSubject());
        // 设置邮件消息发送的时间
        mailMessage.setSentDate(Calendar.getInstance().getTime());
        // 设置邮件消息的主要内容
        mailMessage.setText(mailInfo.getContent());
        
        return mailMessage;
    }
    
    private static Message getImageMessage(Session sendMailSession, MailConfigInfo mailConfigInfo, MailInfo mailInfo, int mailType) throws Exception {
    	// 根据session创建一个邮件消息
        Message mailMessage = new MimeMessage(sendMailSession);
        // 创建邮件发送者地址
        Address from = new InternetAddress(mailConfigInfo.getUsername());
        // 设置邮件消息的发送者
        mailMessage.setFrom(from);

        // 创建邮件的接收者地址 to：发送；cc：抄送
        Address[][] maillToArr = getMailToAddress(mailInfo, mailType);

        // 设置邮件消息的接收者，发送，抄送
        if (maillToArr != null && maillToArr[0] != null && maillToArr[0].length > 0) {
            mailMessage.setRecipients(Message.RecipientType.TO, maillToArr[0]);
        }
        if (maillToArr != null && maillToArr[1] != null && maillToArr[1].length > 0) {
            mailMessage.setRecipients(Message.RecipientType.CC, maillToArr[1]);
        }

        // 设置邮件消息的主题
        mailMessage.setSubject(mailInfo.getSubject());
        // 设置邮件消息发送的时间
        mailMessage.setSentDate(Calendar.getInstance().getTime());

        // MimeMultipart类是一个容器类，包含MimeBodyPart类型的对象
        Multipart multiPart = new MimeMultipart("related");
        // 创建一个包含HTML内容的MimeBodyPart
        BodyPart bodyPart = new MimeBodyPart();
        // 设置html邮件消息内容
//        bodyPart.setContent(mailInfo.getContent(), "text/html; charset=utf-8");
//        multiPart.addBodyPart(bodyPart);

        //添加附件
        if(mailInfo.getAttachFileNames() != null && mailInfo.getAttachFileNames().length != 0){
            for(String attachFile : mailInfo.getAttachFileNames()){
                bodyPart=new MimeBodyPart();
                //得到数据源  
                FileDataSource fds=new FileDataSource(attachFile);
                //得到附件本身并放入BodyPart 
                bodyPart.setDataHandler(new DataHandler(fds));
                //得到文件名并编码（防止中文文件名乱码）同样放入BodyPart
                bodyPart.setFileName(MimeUtility.encodeText(fds.getName()));  
                multiPart.addBodyPart(bodyPart);  
            }
        }
        
        //添加图片
        if(mailInfo.getImageNames() != null && mailInfo.getImageNames().length != 0){
        	for(String imageFile : mailInfo.getImageNames()){
        		//得到数据源
        		FileDataSource fds=new FileDataSource(imageFile);  
        		String cid = fds.getName().replaceAll(".jpg", "");
        		cid = MimeUtility.encodeText(cid);
        		bodyPart = new MimeBodyPart();
        		String html = "<!DOCTYPE html><html><head><meta charset=\"utf-8\"></head>";
        		html += "<body><div id=\"container\" style=\"width:100%; height: 100%;\">";
        		html += "<img src=\"cid:" + cid + "\">";
        		html += "</div></body></html>";
        		bodyPart.setContent(html, "text/html;charset=utf8");
        		multiPart.addBodyPart(bodyPart);
        		
        		bodyPart=new MimeBodyPart();
        		//得到附件本身并放入BodyPart
        		bodyPart.setDataHandler(new DataHandler(fds));
        		//得到文件名并编码（防止中文文件名乱码）同样放入BodyPart
        		bodyPart.setFileName(MimeUtility.encodeText(fds.getName()));  
        		bodyPart.setHeader("Content-ID", "<" + cid + ">");
        		multiPart.addBodyPart(bodyPart);
        	}
        }
        
        // 设置邮件消息的主要内容
        mailMessage.setContent(multiPart);
		
        return mailMessage;
	}
}
